﻿// ------------------------------------------------------------------------------
// <copyright file="ConcatenationConverter.cs" company="EGIT">
//    Copyright (C) 2012 Suresh Kumar T.
//
//    This program is free software; you can redistribute it and/or
//    modify it under the terms of the GNU General Public License
//    as published by the Free Software Foundation version 2
//    of the License.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    http://www.gnu.org/licenses/gpl-2.0.html
//
// </copyright>
// ------------------------------------------------------------------------------

namespace StoreAppLib.Converters
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Data;

    /// <summary>
    /// ConcatenationConverter join as specified field in a collection using specified separator.
    /// </summary>
    public class ConcatenationConverter : IValueConverter
    {
        /// <summary>
        /// Concatenate collection data during binding to string value
        /// </summary>
        /// <param name="value">The value produced by the binding source.</param>
        /// <param name="targetType">The type of the binding target property.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="language">The culture to use in the converter.</param>
        /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            if (value != null && parameter != null && value is IEnumerable<object> && parameter is ConcatenationParameter)
            {
                ConcatenationParameter concatenation = parameter as ConcatenationParameter;

                IEnumerable<object> collection = value as IEnumerable<object>;

                IEnumerable<object> values = this.GetValues(collection, concatenation.Property);

                if (concatenation.Distinct)
                {
                    values = values.Distinct().OrderBy(item => item);
                }
                else
                {
                    values = values.OrderBy(item => item);
                }

                return values.Aggregate(string.Empty, (result, row) => result += (result.Equals(string.Empty) ? string.Empty : concatenation.Separator) + row.ToString());
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Not implemented
        /// </summary>
        /// <param name="value">The value that is produced by the binding target.</param>
        /// <param name="targetType">The type to convert to.</param>
        /// <param name="parameter">The converter parameter to use.</param>
        /// <param name="language">The culture to use in the converter.</param>
        /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            return DependencyProperty.UnsetValue; 
        }

        /// <summary>
        /// Retrieves collection of elements to be concatenated from collection passed.
        /// This function will be recursively called in case of nested collection.
        /// </summary>
        /// <param name="value">Collection to be searched</param>
        /// <param name="property">Property to be extracted</param>
        /// <returns>Collection of elements to be concatenated</returns>
        private IEnumerable<object> GetValues(IEnumerable<object> value, string property)
        {
            string[] propertyArray = property.Split('.');

            if (propertyArray.Length > 1)
            {
                IEnumerable<object> result = value.Select<object, object>(item => item.GetType().GetRuntimeProperty(propertyArray[0]).GetValue(item));

                List<object> collection = new List<object>();

                foreach (object item in result)
                {
                    if (item is IEnumerable<object>)
                    {
                        collection = collection.Concat(item as IEnumerable<object>).ToList();
                    }
                    else
                    {
                        collection.Add(item);
                    }
                }

                return this.GetValues(collection, string.Join(".", propertyArray, 1, propertyArray.Length - 1));
            }
            else
            {
                return value.Select<object, object>(item => item.GetType().GetRuntimeProperty(property).GetValue(item));
            }
        }
    }
}
